<?php
class System_Plugin_Action_Default {

	const EVENT_ON_INIT			= 1;
	const EVENT_ON_RESPONSE = 2;
	const EVENT_ON_COMPLETE = 3;

	/**
	 * eindeutige Identifikation der Aktion, wird in der Datenbank als hid abgespeichert
	 * @var string $_hid
	 */
	protected $_hid = null;

	/**
	 * Daten der Aktion, die gerade verarbeitet wird
	 * @var object Application_Model_Schema_Action
	 */
	protected $_actionData;

	/**
	 * Daten, der Resourcen, auf die eine Aktion waehrend der Verarbeitung zugreift
	 * @var array $_resourceData
	 */
	protected $_resourceData;

	/**
	 * Daten, der Objekte, auf die eine Aktion waehrend der Verarbeitung zugreift
	 * @var array $_objectData
	 */
	protected $_objectData;

	/**
	 * Aufgrund dieser Methodenname, wird nach Methoden zur Verarbeitungszeit, zur
	 * Verarbeitungsmenge und Verarbeitungsendresultat gesucht
	 * @var string $_executionMethods
	 */
	protected $_executionMethods = array('Action', 'Resources', 'Objects');

	/**
	 * EventCode, damit jederzeit beim Verarbeiten abgefragt werden kann, in
	 * welchem Stadion der Event sich befindet
	 * @var string $_eventCode
	 */
	private $_eventCode = 0;

	/**
	 * Viele Aktionen laufen auf Zeitbasis. Die bereits verwendete Zeit wird in der Variable
	 * _elapsedTime abgespeichert
	 * @var string $_elapsedTime
	 */
	private $_elapsedTime = null;

	/**
	 * Konstruktor der Klasse System_Plugin_Action_Default
	 * @param none
	 * @return none;
	 */
	public function __construct() {
	}

	/**
	 * Lade die Aktionsdatensdaten anhand der hid und schreibe sie in die Variable _actionData
	 * @param none
	 * @return none
	 */
	protected function _prepareActionData() {
		if($this->_hid === null) { // @todo: eigene Exection erstellen
			throw new Exception('hid in System Action Plugin darf nicht leer sein', 0);
			return false;
		}

		$mAction	= new Application_Model_Actions();
		$dAction	= $mAction->find(array('actions.hid = ?' => $this->_hid), array('ActionValueResources', 'ActionValueObjects'));

		if($dAction instanceof Application_Model_Schema_Action) {
			$this->_actionData = $dAction;
		}
	}

	/**
	 * liefert die verwendeten Aktionsdaten
	 * @param none
	 * @return object $dResource
	 */
	public function getActionData() {
		return $this->_actionData;
	}

	/**
	 * speichert eine Resource, die in einer Aktion verwendet wird, in _resourceData, um sie
	 * spaeteren Methoden zur Verarbeitung zur Verfuegung zu stellen
	 * @param object $dResource
	 * @return none
	 */
	public function setResourceData($dResource) {
		if(is_array($dResource)) {
			array_walk($dResource, array($this, 'setResourceData'));
			return true;
		}

		// @todo eventuell kann der Aufbau hier noch anders sein -> $dResource->id
		$this->_resourceData[$dResource->resource->id] = $dResource;
	}

	/**
	 * speichert ein Objekt, das in einer Aktion verwendet wird, in _objectData, um sie
	 * spaeteren Methoden zur Verarbeitung zur Verfuegung zu stellen
	 * @param object $dObject
	 * @return none
	 */
	public function setObjectData($dObject) {
		if(is_array($dObject)) {
			array_walk($dObject, array($this, 'setObjectData'));
			return true;
		}

		$this->_objectData[$dObject->object->id] = $dObject;
	}	

	/**
	 * liefert die Resourcedaten, die von einer Aktion geladen und verwendet werden
	 * @param int $id Id einer Resource; optionaler Parameter wenn nicht angegeben wird
	 *	das ganze Array zurueckgeliefert
	 * @return object $dResource
	 */
	public function getResourceData($id = null) {
		if($id !== null) {
			if($this->_resourceData !== null && array_key_exists($id, $this->_resourceData)) {
				return $this->_resourceData[$id];
			}
			return null;
		}

		return $this->_resourceData;
	}

	/**
	 * liefert die Objektdaten, die von einer Aktion geladen und verwendet werden
	 * @param none
	 * @return object $dResource
	 */
	public function getObjectData() {
		return $this->_objectData;
	}

	/**
	 * setzt die bisher in der Aktion abgelaufene Zeit
	 * @param int $time ablaufene Sekunden, die auf bisherige Zeit aufgerechnet werden
	 * @return object Instanz auf System_Plugin_Action_Default
	 */
	public function setElapsedTime($time) {
		if($this->_elapsedTime === null) {
			$this->_elapsedTime = 0;
		}
		$this->_elapsedTime += $time;

		return $this;
	}

	/**
	 * lieftert die bisher abgelaufene Zeit
	 * @param none
	 * @return int aktuell gesetzter EventCode
	 */
	public function getElapsedTime() {
		return $this->_elapsedTime;
	}

	/**
	 * berechnet die Dauer der Aktion, anhand des Resourcenverbrauchs
	 * @param none
	 * @return int Anzahl, in Sekunden, bis die erste Resource verbraucht ist
	 */
	protected function _getResourcesExecutionTime() {
		$durcationSecond = null;

		if($this->_actionData->use->resources !== null) {
			foreach($this->_actionData->use->resources as $actionResource) {

				if($actionResource->rate !== null) {
					if(($dResource = $this->getResourceData($actionResource->id)) === null) {
						throw new Exception('benoetige Resource wurde nicht gesetzt', 0);
						return 0;
					}

					$reduceRate				= $actionResource->rate / 60; // Rate der Resource in der Sekunden
					$resourceCurValue	= $dResource->resource->curValue; // aktuelle Einheiten des Rohstoffs

					// Berechne und aktuellisiere $durationSecond solange, bis die kleinste Berechnung gefunden wurde
					if($durcationSecond === null || ($resourceCurValue / $reduceRate) < $durcationSecond) {
						$durcationSecond = $resourceCurValue / $reduceRate;
					}
				}
			}
		}

		return $durcationSecond;
	}

	/**
	 * berechnet die Dauer der Aktion, anhand der verwendeten Objekte (im Normal handelt
	 * es sich bei Objektaktionen um zeitlich festgelegte Aktion -> _getActionExecutionTime
	 * @param none
	 * @return int Anzahl, in Sekunden, bis die erste Resource verbraucht ist
	 */
	protected function _getObjectsExecutionTime() {
		// momentan faellt mir keine richtige Option zum verwenden ein
		return null;
	}

	/**
	 * berechnet die Dauer der Aktion, anhand der festgelegten Dauer einer Aktion
	 * @param none
	 * @return int Anzahl, in Sekunden, bis die erste Resource verbraucht ist
	 */
	protected function _getActionExecutionTime() {
		$durcationSecond = null;

		if($this->_elapsedTime !== null) {
			$durcationSecond = $this->_actionData->executionTime - $this->_elapsedTime;
		}

		return $durcationSecond;
	}

	/**
	 * durchlaeuft alle Berechnungsmethoden, die angeben wurden, und gibt die maximale
	 * Zeitspanne, die fuer die Verarbeitung der Aktion vorhanden ist
	 * @param none
	 * @return int Anzahl der Sekunden bis die Aktion beendet sein wird
	 */
	public function getExecutionTime() {
		$secToExecution			= null;
		$tmpSecToExecution	= null;

		foreach($this->_executionMethods as $type) {
			$method	= '_get' . ucfirst($type) . 'ExecutionTime';

			if(method_exists($this, $method)) {
				$tmpSecToExecution	= $this->$method();
			}

			if($tmpSecToExecution !== null && ($secToExecution === null || $secToExecution > $tmpSecToExecution)) {
				$secToExecution = $tmpSecToExecution;
			}
		}
		return $secToExecution;
	}

	/**
	 * berechnet den Verbrauch / die Erzeugung einer Resource in dem uebergebenen Zeitraum
	 * @param int Zeitraum in Sekunden
	 * @return boolean ist ein Queue-Eintrag aufgrund des Resourcenverbrauchs bzw. -gewinns
	 *	abgearbeitet wird true zurueckgeliefert, anonsten false
	 */
	protected function _executeResourcesResponse($allottedTime) {
		$queueComplete = false;

		foreach(array('use', 'produce') as $type) {
			if(isset($this->_actionData->{$type})) {
				foreach($this->_actionData->{$type}->resources as $actionResource) {

					// es werden nur Resourcen mit einer Rate verwendet
					if($actionResource->rate !== null) {

						// wenn keine Daten der Resource zur Berechnung vorliegen, kann der Eintrag nicht
						// verarbeitet werden
						if(($dResource = $this->getResourceData($actionResource->id)) === null) {
							throw new Exception('benoetige Resource wurde nicht gesetzt', 0);
							return 0;
						}

						$reduceRate				= $actionResource->rate / 60; // Rate der Resource in der Sekunden
						$resourceUseValue	= $reduceRate * $allottedTime;

						// Berechnung um wieviel Einheiten eine beteiligte Resource abnimmt bzw. zunimmt
						if($type === 'use') {
							$resourceEndValue	= $dResource->resource->curValue - $resourceUseValue;

						} elseif($type === 'produce') {
							$resourceEndValue	= $dResource->resource->curValue + $resourceUseValue;
						}

						// kann eigentlich garnicht sein
						// TODO hier sollte ein Naerhungswert vorhanden(z.B.: 0.1 = 0)
						if($resourceEndValue <= 0) {
							$resourceEndValue = 0;
							$queueComplete		= true;
						}

						$dResource->resource->curValue = $resourceEndValue;

						// Speicherung des neuen Standes der Einheiten je nach Resourcetyp in dem jeweiligen
						// Model
						// @todo: die Resourcendaten sollten dem Queue Model zurueckgeliefert werden und dort
						// gespeichert werden
						if($dResource instanceof Application_Model_Schema_StorageResource) {
							$mResource = new Application_Model_StorageResources();
						}

						if($dResource instanceof Application_Model_Schema_TileResource) {
							$mResource = new Application_Model_TileResources();
						}

						$mResource->save($dResource);
					}
				}
			}
		}
		return $queueComplete;
	}

	/**
	 * berechnet den Stand, den eine Objekt innerhalb eines Zeitraums ereicht hat
	 * @param int Zeitraum in Sekunden
	 * @return boolean ist ein Queue-Eintrag aufgrund des Zeitraums	abgearbeitet wird true
	 *	zurueckgeliefert, anonsten false
	 */
	protected function _executeObjectsResponse($allottedTime) {
		$queueComplete = false;
		return $queueComplete;
	}

	/**
	 * berechnet den aktuellen Stand einer Aktion, nach dem uebergebenen Zeitraum
	 * @param int $allottedTime Zeitraum in Sekunden
	 * @return boolean ist die Aktion aufgrund des uebergebenen Zeitraums abgearbeitet wird
	 *	true zurueckgeliefert, anonsten false
	 */
	protected function _executeActionResponse($allottedTime) {
		$executionComplete = false;

		// Berechnung der Zeit, die bisher auf die Aktion verwendet wurde und Speicherung in
		// _elapsedTime, damit die Queue darauf zugreifen kann
		$this->_elapsedTime = $this->_elapsedTime + $allottedTime;

		// Wenn die abgelaufene Zeit der Ausfuehrungszeit entspricht, setze die Aktion als
		// abgearbeitet
		if($this->_elapsedTime >= $this->_actionData->executionTime) {
			$executionComplete = true;
		}

		return $executionComplete;
	}

	/**
	 * durchlaeuft alle Berechnungsmethoden, die den Resourcen-, Energie- usw. Verbrauch
	 * berechnen, die innerhalb des Zeitraums benoetigt werden
	 * @param int $allottedTime Anzahl der Sekunden die zur Berechnung zur Verfuegung stehen
	 * @return boolean wenn die Queue durch die Berechnungen abgeschlossen wurde true,
	 *	ansonsten	false
	 */
	public function execute($allottedTime) {
		$actionComplete = false;

		foreach($this->_executionMethods as $type) {
			$method	= '_execute' . ucfirst($type) . 'Response';

			if(method_exists($this, $method)) {
				if($this->$method($allottedTime) === true) {
					$this->onComplete();
					$actionComplete = true;
				}
			}
		}

		return $actionComplete;
	}

	/**
	 * setzt den EventCode, damit jederzeit beim Verarbeiten abgefragt werden kann, in
	 * welchem Stadion der Event sich befindet
	 * @param int $eventCode
	 * @return none
	 */
	protected function _setEventCode($eventCode) {
		$this->_eventCode = $eventCode;
	}

	/**
	 * lieftert den aktuell gesetzten EventCode
	 * @param none
	 * @return int aktuell gesetzter EventCode
	 */
	protected function _getEventCode() {
		return $this->_eventCode;
	}

	/**
	 * diese Methode sollte aufgerufen wenn, die Aktion initialisert wird und wird im
	 * Normalfall von der ableitenden Klasse ueberschrieben
	 * @param none
	 * @param object Instanz auf System_Plugin_Action_Default
	 */
	public function onInit() {
		$this->_setEventCode(System_Plugin_Action_Default::EVENT_ON_INIT);
		return $this;
	}

	/**
	 * diese Methode wird aufgerufen, wenn die Aktion sich in der Verarbeitung befindet. Im
	 * Normalfall wird sie von der ableitenden Klasse ueberschrieben
	 * @param none
	 * @param object Instanz auf System_Plugin_Action_Default
	 */
	public function onResponse() {
		$this->_setEventCode(System_Plugin_Action_Default::EVENT_ON_RESPONSE);
		return $this;
	}

	/**
	 * diese Methode wird aufgerufen, wenn die Aktion korrekt beendet wurde. Wird verwendet
	 * um abschliessende Arbeiten vorzunehmen
	 * @param none
	 * @param object Instanz auf System_Plugin_Action_Default
	 */
	public function onComplete() {
		$this->_setEventCode(System_Plugin_Action_Default::EVENT_ON_COMPLETE);
		return $this;
	}
}
?>